import { computed, defineComponent, PropType, reactive, watch, watchEffect } from "vue";
import VirtualList from "../virtual-list";
import formFieldRef from "../use/formFieldRef";
import Checkbox from "../inner/Checkbox";
import Text from "../Typography/Text";
import { FeatherSearch, FeatherX } from "cui-vue-icons/feather";
import Button from "../Button";
import Input from "../FormElements/Input";

export interface TeleportBoxItem {
    value: any
    label: any
    disabled?: boolean
    checked?: boolean
    children?: TeleportBoxItem[]
    [key: string]: any
}

export interface TeleportBoxProps {
    data?: TeleportBoxItem[]
    modelValue?: any[]
    defaultValue?: any[]
    disabled?: boolean
    virtual?: boolean
    onChange?: (value: any[]) => void
    renderSourceItem?: (item: TeleportBoxItem, onChange: (checked: boolean) => void) => JSX.Element
    renderSelectedItem?: (item: TeleportBoxItem, onRemove: () => void) => JSX.Element
    filter?: (item: TeleportBoxItem, keyword: string) => boolean
}

export default defineComponent({
    name: 'TeleportBox',
    props: {
        data: { type: Array as PropType<TeleportBoxProps['data']>, default: () => []},
        modelValue: { type: Array as PropType<TeleportBoxProps['modelValue']>, default: () => []},
        defaultValue: { type: Array as PropType<TeleportBoxProps['defaultValue']>},
        disabled: { type: Boolean as PropType<TeleportBoxProps['disabled']>},
        virtual: { type: Boolean as PropType<TeleportBoxProps['virtual']>},
        renderSourceItem: { type: Function as PropType<TeleportBoxProps['renderSourceItem']>},
        renderSelectedItem: { type: Function as PropType<TeleportBoxProps['renderSelectedItem']>},
        filter: { type: Function as PropType<TeleportBoxProps['filter']>},
    },
    emits: ['update:modelValue', 'change'],
    setup (props, { emit, slots }) {
        const classList = computed(() => ({
            'cm-teleport-box': true,
            'cm-teleport-box--disabled': props.disabled
        }));
        const value = formFieldRef<any[]>(props, emit, []);
        const store = reactive({
            leftList: [] as TeleportBoxItem[],
            rightList: [] as TeleportBoxItem[],
            originList: [] as TeleportBoxItem[],
            flatList: [] as TeleportBoxItem[],
            dataMap: {},
            checkedAll: false
        });

        const onFilter = (v: any) => {
            if (v) {
                const list = store.flatList?.filter(item => {
                    if (props.filter) {
                        return props.filter(item, v);
                    }
                    if (item.children?.length) {
                        const childs = item.children?.filter(sub => (item.label as string).includes(v));
                        return childs?.length;
                    }
                    return (item.label as string).includes(v);
                });
                store.leftList = list || [];
            } else {
                store.leftList = store.flatList || [];
            }
        };

        const initData = () => {
            const map: { [key: string]: TeleportBoxItem } = {};
            let originList: TeleportBoxItem[] = [];
            const vals = value.value;
            if (props.defaultValue) {
                props.defaultValue.forEach(v => {
                    if (!vals.includes(v)) {
                        vals.push(v);
                    }
                });
            }
            const list = props.data?.flatMap(item => {
                if (item.children?.length) {
                    originList = originList.concat(item.children);
                } else {
                    originList.push(item);
                }
                return [item, ...(item.children ? item.children : [])].flat();
            });
            list?.forEach(item => {
                if (vals.includes(item.value)) {
                    item.checked = true;
                }
                map[item.value] = item;
            });
            store.originList = originList;
            store.flatList = list || [];
            store.dataMap = map;
            store.leftList = list || [];

            value.value = [...vals];
        };

        initData();

        watch(() => props.data, () => {
            initData();
        });

        watchEffect(() => {
            const val = value.value;
            const total = store.originList.filter(item => !item.disabled || (item.disabled && item.checked)).length;
            if (props.data?.length && val.length === total) {
                store.checkedAll = true;
            } else {
                store.checkedAll = false;
            }
            const arr = val.map(v => {
                return (store.dataMap as any)[v];
            });
            store.rightList = arr;
        });

        const onChecked = (checked: boolean, item: TeleportBoxItem) => {
            if (props.disabled || item.disabled) {
                return;
            }
            item.checked = checked;
            let val: any[] = value.value || [];
            const v = item.value;
            if (checked) {
                if (!val.includes(v)) {
                    val = val.concat(v);
                }
            } else {
                const index = val.indexOf(v);
                if (index > -1) {
                    val.splice(index, 1);
                }
            }
            value.value = [...val];
            emit('change', value.value);
        };

        const onRemoveItem = (item: TeleportBoxItem) => {
            if (item.disabled) {
                return;
            }
            item.checked = false;
            const val = value.value;
            const index = val.indexOf(item.value);
            if (index > -1) {
                val.splice(index, 1);
            }
            value.value = [...val];
            emit('change', value.value);
        };

        const clearAll = () => {
            const vals = value.value;
            const restVals: any[] = [];
            vals.forEach(val => {
                const item = (store.dataMap as any)[val];
                if (item.disabled) {
                    restVals.push(item.value);
                    return;
                }
                item.checked = false;
            });
            value.value = restVals;
            emit('change', restVals);
        };

        const selectAll = () => {
            const vals: any[] = [];
            for (const val in store.dataMap) {
                const item = (store.dataMap as any)[val];
                if (item.children?.length) {
                    continue;
                }
                if (item.disabled) {
                    if (item.checked) {
                        vals.push(item.value);
                    }
                    continue;
                }
                item.checked = true;
                vals.push(item.value);
            }
            value.value = vals;
            emit('change', vals);
        };

        const renderSourceList = () => {
            return store.leftList.map((item, index) => {
                if (item.children?.length) {
                    return <div class="cm-teleport-group-title">{item.label}</div>;
                }
                return props.renderSourceItem?.(item, (checked: boolean) => {
                    onChecked(checked, item);
                }) || <Checkbox key={item.value} disabled={item.disabled} checked={item.checked} onChange={(v) => onChecked(v, item)} label={item.label}/>;
            });
        };

        const renderTargetList = () => {
            return value.value.map((val) => {
                const item: TeleportBoxItem = (store.dataMap as any)[val];
                if (!item) return null;
                return props.renderSelectedItem?.(item, () => {
                    onRemoveItem(item);
                }) || <div class="cm-teleport-right-item">
                    <Text>{item.label}</Text>
                    {
                        !item.disabled
                            ? <FeatherX class="cm-teleport-right-item-close" onClick={() => onRemoveItem(item)}/>
                            : null
                    }
                </div>;
            });
        };

        return () => <div class={classList.value}>
            <section class="cm-teleport-left">
                <div class="cm-teleport-header">
                    <span class="cm-teleport-header-total">总个数：{store.originList?.length}</span>
                    {
                        store.checkedAll
                            ? <Button size="small" theme="borderless" class="cm-teleport-select-all" onClick={clearAll}>取消全选</Button>
                            : <Button size="small" theme="borderless" class="cm-teleport-select-all" onClick={selectAll}>全选</Button>
                    }
                </div>
                <div class="cm-teleport-filter">
                    <Input suffix={<FeatherSearch/>} asFormField={false} trigger="input" onChange={onFilter} placeholder="搜索" clearable/>
                </div>
                <div class="cm-teleport-left-list">
                    {
                        props.virtual
                            ? <VirtualList items={store.leftList} itemEstimatedSize={30}>
                                {{
                                    default: (scope) => {
                                        return <SourceItem
                                            item={scope.item as TeleportBoxItem}
                                            renderSourceItem={props.renderSourceItem}
                                            onChecked={onChecked} />;
                                    }
                                }}
                            </VirtualList>
                            : renderSourceList()
                    }
                </div>
            </section>
            <section class="cm-teleport-right">
                <div class="cm-teleport-header cm-teleport-right-header">
                    <span class="cm-teleport-header-total">已选个数：{value.value.length}</span>
                    {
                        value.value.length
                            ? <Button size="small" theme="borderless" class="cm-teleport-clear" onClick={clearAll}>清空</Button>
                            : null
                    }
                </div>
                <div class="cm-teleport-right-list">
                    {
                        props.virtual
                            ? <VirtualList items={store.rightList} itemEstimatedSize={30}>
                                {{
                                    default: (scope) => {
                                        return <TargetItem
                                            item={scope.item as TeleportBoxItem}
                                            renderSelectedItem={props.renderSelectedItem}
                                            onRemoveItem={onRemoveItem} store={store}/>;
                                    }
                                }}
                            </VirtualList>
                            : renderTargetList()
                    }
                </div>
            </section>
        </div>;
    }
});

const SourceItem = (props: any) => {
    const item = props.item;
    return item.children?.length
        ? <div class="cm-teleport-group-title">{item.label}</div>
        : <div>
            {
                props.renderSourceItem?.(item, (checked: boolean) => {
                    props.onChecked?.(checked, item);
                }) || <Checkbox ref={props.ref} disabled={item.disabled} checked={item.checked} onChange={(v) => props.onChecked(v, item)} label={item.label}/>
            }
        </div>;
};

const TargetItem = (props: any) => {
    const item: TeleportBoxItem = props.item;
    return item ?
        <div>
            {
                props.renderSelectedItem?.(item, () => {
                    props.onRemoveItem?.(item);
                }) || <div class="cm-teleport-right-item">
                    <Text>{item.label}</Text>
                    {
                        !item.disabled
                            ? <FeatherX class="cm-teleport-right-item-close" onClick={() => props.onRemoveItem?.(item)}/>
                            : null
                    }
                </div>
            }
        </div> : null;
};
